package com.easylinkin.linkappapi.building.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.building.constant.FloorConstant;
import com.easylinkin.linkappapi.building.entity.Floor;
import com.easylinkin.linkappapi.building.entity.vo.FloorVo;
import com.easylinkin.linkappapi.building.entity.vo.SimpleBatchFloorVo;
import com.easylinkin.linkappapi.building.mapper.FloorMapper;
import com.easylinkin.linkappapi.building.service.FloorService;
import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.common.service.CommonService;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.location.vo.AppFloorFileVO;
import com.easylinkin.linkappapi.machinery.entity.ElevatorFloorRecord;
import com.easylinkin.linkappapi.machinery.service.ElevatorFloorRecordService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;

/**
 * Floor表服务实现类
 *
 * @author CodeGenerator
 * @date 2022/09/21
 */
@Slf4j
@Service("appFloorService")
public class FloorServiceImpl extends ServiceImpl
        <FloorMapper, Floor> implements FloorService {
    @Resource
    private CommonService commonService;
    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private ElevatorFloorRecordService elevatorFloorRecordService;


    @Override
    public boolean saveOneForInit(Floor appFloor) {
        appFloor.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        validParamRequired(appFloor);
        validParamFormat(appFloor);
        commonService.setCreateAndModifyInfo(appFloor);
        return save(appFloor);
    }

    /**
     * 恢复楼层的排序
     *
     * @param floors 楼层列表,已经按照楼层高低排好序了
     * @return firstFloorIndex首层的index
     */
    private int recoverSortAndHeight(List<Floor> floors) {
        int firstFloorIndex = findFirstFloorIndex(floors);
//        首层以上
        for (int i = firstFloorIndex + 1; i < floors.size(); i++) {
//            第i层
            Floor fi = floors.get(i);
//            before i 层
            Floor bfi = floors.get(i - 1);
            fi.setSortNo(i - firstFloorIndex);
            fi.setBuildingStandardHeight(BigDecimal.valueOf(bfi.getBuildingStandardHeight()).add(BigDecimal.valueOf(bfi.getBuildingFloorHeight())).floatValue());
            fi.setStructureStandardHeight(BigDecimal.valueOf(bfi.getStructureStandardHeight()).add(BigDecimal.valueOf(bfi.getStructureFloorHeight())).floatValue());
        }
//        首层以下
        for (int i = firstFloorIndex - 1; i > -1; i--) {
//            第i层
            Floor fi = floors.get(i);
//            before i 层
            Floor bfi = floors.get(i + 1);
            fi.setSortNo(i - firstFloorIndex);
            fi.setBuildingStandardHeight(BigDecimal.valueOf(bfi.getBuildingStandardHeight()).subtract(BigDecimal.valueOf(fi.getBuildingFloorHeight())).floatValue());
            fi.setStructureStandardHeight(BigDecimal.valueOf(bfi.getStructureStandardHeight()).subtract(BigDecimal.valueOf(fi.getStructureFloorHeight())).floatValue());
        }
        return firstFloorIndex;
    }

    private int findFirstFloorIndex(List<Floor> floors) {
        for (int i = 0; i < floors.size(); i++) {
            if (FloorConstant.TypeEnum.FIRST.getType().equals(floors.get(i).getType())) {
                return i;
            }
        }
        throw new BusinessException("没有找到首层");
    }

    @Override
    public boolean saveOne(Floor appFloor) {
        appFloor.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        validParamRequired(appFloor);
        validRepeat(appFloor);
        validParamFormat(appFloor);
//        其他楼层
        appFloor.setType(FloorConstant.TypeEnum.COMMON.getType());

        Floor floor = new Floor();
        List<Floor> floors = selectPage(floor.setBuildingId(appFloor.getBuildingId()));
        Collections.reverse(floors);
//        点击的那个新建按钮，基于谁新建
        final int baseId = appFloor.getBaseId();
        int baseIndex = 0;
        for (int i = 0; i < floors.size(); i++) {
            if (floors.get(i).getId().equals(baseId)) {
                baseIndex = i;
                break;
            }
        }
        commonService.setCreateAndModifyInfo(appFloor);
        floors.add(baseIndex + 1, appFloor.setId(null));
        recoverSortAndHeight(floors);
        saveOrUpdateBatch(floors);
//        可以优化只更新部分
//        int firstFloorIndex = recoverSortAndHeight(floors);
//        if (baseId>=firstFloorIndex) {
//            saveOrUpdateBatch(floors.subList());
//        }
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOne(Floor appFloor) {
        Assert.notNull(appFloor.getId(), "id不能为空");
        commonService.setModifyInfo(appFloor);
        appFloor.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        validRepeat(appFloor);
        validParamFormat(appFloor);

        Floor floor = new Floor();
        List<Floor> floors = selectPage(floor.setBuildingId(appFloor.getBuildingId()));
        Collections.reverse(floors);

        for (int i = 0; i < floors.size(); i++) {
            if (floors.get(i).getId().equals(appFloor.getId())) {
                floors.set(i, appFloor);
            }
        }
        recoverSortAndHeight(floors);
        saveOrUpdateBatch(floors);
        return true;
    }

    @Override
    public IPage<Floor> selectPage(Page page, Floor appFloor) {
        appFloor.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return baseMapper.selectPage(page, appFloor);
    }

    @Override
    public List<Floor> selectPage(Floor appFloor) {
        appFloor.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return baseMapper.selectPage(appFloor);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByIds(List<Integer> idList) {
        if (ObjectUtils.isEmpty(idList)) {
            return true;
        }
        checkBeUsed(idList);

        LambdaQueryWrapper<Floor> floorWrapper = new LambdaQueryWrapper<>();
        floorWrapper.in(Floor::getId, idList).in(Floor::getType, 0, 1);
        Assert.isTrue(this.count(floorWrapper) == 0, "首层和基础层不允许删除");

        Floor floor = getOneById(idList.get(0));
        if (floor != null) {
            List<Floor> floors = selectPage(floor.setBuildingId(floor.getBuildingId()));
            Collections.reverse(floors);
            Iterator<Floor> it = floors.iterator();
            while (it.hasNext()) {
                Floor fl = it.next();
                if (idList.contains(fl.getId())) {
                    it.remove();
                }
            }
            removeByIds(idList);
            recoverSortAndHeight(floors);
            saveOrUpdateBatch(floors);
        }
        return true;
    }

    @Override
    public void checkBeUsed(Collection<Integer> idList) {
        LambdaQueryWrapper<ElevatorFloorRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ElevatorFloorRecord::getFloorId, idList);
        Assert.isTrue(elevatorFloorRecordService.count(wrapper) == 0, "该楼层已被升降机驻留分析引用，不能删除");
    }

    @Override
    public void export(Floor appFloor, HttpServletRequest request, HttpServletResponse
            response) {

        IPage<Floor> page = selectPage(new Page(0, -1), appFloor);
        List<Floor> records = page.getRecords();
        List
                <FloorVo> appFloorVos = new ArrayList<>();
        for (Floor expert : records) {
            appFloorVos.add(new FloorVo(expert));
        }

        String keyValue = "名称:name,租户id:tenantId";
        String title = "Floor导出数据";
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportExcel(outputStream, keyValue, appFloorVos, ExcelConstant.XLS, title);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }
    }

    @Override
    public Floor getOneById(Serializable id) {
        return baseMapper.getOneById(id);
    }

    /**
     * 校验重复
     */
    private void validRepeat(Floor appFloor) {
        LambdaQueryWrapper<Floor> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Floor::getName, appFloor.getName());
        queryWrapper.eq(Floor::getBuildingId, appFloor.getBuildingId());
        List<Floor> list = baseMapper.selectList(queryWrapper);
        if (list.size() == 0) {
            return;
        }
        if (list.size() > 1) {
            throw new BusinessException("名称有重复");
        }
        if (ObjectUtils.isEmpty(appFloor.getId())) {
            throw new BusinessException("名称已存在");
        }
        if (!appFloor.getId().equals(list.get(0).getId())) {
            throw new BusinessException("名称已存在");
        }
    }


    /**
     * 校验参数必填
     */
    private void validParamRequired(Floor appFloor) {
        Assert.notNull(appFloor, "参数为空");
        Assert.isTrue(ObjectUtils.isNotEmpty(appFloor.getName()), "名称为空");
        Assert.isTrue(ObjectUtils.isNotEmpty(appFloor.getBuildingId()), "单体id为空");
        Assert.isTrue(ObjectUtils.isNotEmpty(appFloor.getBuildingFloorHeight()), "建筑层高为空");
        Assert.isTrue(ObjectUtils.isNotEmpty(appFloor.getStructureFloorHeight()), "结构层高为空");
        if (appFloor.getType() != null && appFloor.getType().equals(FloorConstant.TypeEnum.FIRST.getType())) {
            Assert.isTrue(ObjectUtils.isNotEmpty(appFloor.getStructureStandardHeight()), "结构底标高为空");
            Assert.isTrue(ObjectUtils.isNotEmpty(appFloor.getBuildingStandardHeight()), "建筑底标高为空");
        }
    }

    /**
     * 校验参数格式
     */
    private void validParamFormat(Floor appFloor) {
        Assert.isTrue(appFloor.getName() == null || appFloor.getName().length() <= 30,
                "名称超长");
    }

    @Override
    public void initFloorForBuilding(Integer buildingId) {
        Floor floor1 = new Floor(FloorConstant.TypeEnum.FIRST.getName(), buildingId, FloorConstant.TypeEnum.FIRST.getType(), 0, 3F, 3F, 0F, 0F);
        Floor floor0 = new Floor(FloorConstant.TypeEnum.BASE.getName(), buildingId, FloorConstant.TypeEnum.BASE.getType(), -1, 3F, 3F, -3F, -3F);
        saveOneForInit(floor1);
        saveOneForInit(floor0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertBatch(List<Floor> appFloors) {
        for (Floor appFloor : appFloors) {
            saveOne(appFloor);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean simpleInsertBatch(List<SimpleBatchFloorVo> floorVos) {
        Assert.isTrue(ObjectUtils.isNotEmpty(floorVos), "参数为空");
        Assert.isTrue(ObjectUtils.isNotEmpty(floorVos.size() < 3), "参数个数需少于3");

        Floor floor = new Floor();
        List<Floor> floors = selectPage(floor.setBuildingId(floorVos.get(0).getBuildingId()));
        if (ObjectUtils.isEmpty(floors)) {
            throw new BusinessException("根据单体id查询不到首层基础层，请检查单体id参数");
        }
        Collections.reverse(floors);
        Floor basic = floors.remove(0);

        int firstIndex = findFirstFloorIndex(floors);
        List<Integer> deleteIds = new LinkedList<>();

        for (int i = 0; i < floorVos.size(); i++) {
            SimpleBatchFloorVo floorVo = floorVos.get(i);
            if (floorVo.getOverFirstFlag()) {
                for (int num = 1; num <= floorVo.getFloorNum(); num++) {
                    if ((firstIndex + num) < floors.size() && (firstIndex + num) > -1) {
                        deleteIds.add(floors.get(firstIndex + num).getId());
                        floors.set(firstIndex + num, new Floor((num + 1) + "层", floorVo.getBuildingId(), FloorConstant.TypeEnum.COMMON.getType(), floorVo.getBuildingFloorHeight(), floorVo.getStructureFloorHeight()));
                    } else {
                        floors.add(new Floor((num + 1) + "层", floorVo.getBuildingId(), FloorConstant.TypeEnum.COMMON.getType(), floorVo.getBuildingFloorHeight(), floorVo.getStructureFloorHeight()));
                    }
                }

            } else {
                for (int num = 1; num <= floorVo.getFloorNum(); num++) {
                    if ((firstIndex - num) < floors.size() && (firstIndex - num) > -1) {
                        deleteIds.add(floors.get(firstIndex - num).getId());
                        floors.set(firstIndex - num, new Floor(((-1) * num) + "层", floorVo.getBuildingId(), FloorConstant.TypeEnum.COMMON.getType(), floorVo.getBuildingFloorHeight(), floorVo.getStructureFloorHeight()));
                    } else {
                        floors.add(0, new Floor(((-1) * num) + "层", floorVo.getBuildingId(), FloorConstant.TypeEnum.COMMON.getType(), floorVo.getBuildingFloorHeight(), floorVo.getStructureFloorHeight()));
                    }
                }
            }
        }
        if (!deleteIds.isEmpty()) {
            checkBeUsed(deleteIds);
            removeByIds(deleteIds);
        }
        floors.add(0, basic);
        recoverSortAndHeight(floors);
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        Set<String> names = new HashSet<>();
        for (Floor floor1 : floors) {
            floor1.setTenantId(tenantId);
            names.add(floor1.getName());
        }
        Assert.isTrue(names.size() == floors.size(), "名称有重复");
        saveOrUpdateBatch(floors);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOnlyOne(Floor appFloor) {
        Assert.notNull(appFloor.getId(), "id不能为空");
        commonService.setModifyInfo(appFloor);
        appFloor.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        validRepeat(appFloor);
        validParamFormat(appFloor);
        return updateById(appFloor);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBatch(AppFloorFileVO appFloorFileVO) {
        Assert.notNull(appFloorFileVO.getFloors(), "楼层id不能为空");
        Assert.notNull(appFloorFileVO.getFileUrl(), "图纸url不能为空");
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        List<Floor> floors = new ArrayList<>();
        String[] floorList = appFloorFileVO.getFloors().split(",");
        for(String floorStr : floorList) {
            Floor floor = new Floor();
            floor.setId(Integer.parseInt(floorStr));
            floor.setTenantId(tenantId);
            floor.setFileUrl(appFloorFileVO.getFileUrl());
            floors.add(floor);
        }
        return updateBatchById(floors);
    }

}

